﻿using Log;
using SmileSolver;
using Solver.ModelView;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using System.Windows;

namespace Solver
{
    public delegate HalfSmile SmileDelegate();

    public class SolutionCrossTask : AbstractViewModel, ISolutionTask
    {
        ILogger _log;
        public SolutionCrossTask(ILogger log)
        {
            _log = log;
        }

        ~SolutionCrossTask()
        {
            //Debug.WriteLine("SolutionCrosTask destructor:"+_start.ID);
        }

        public void Start()
        {
            State = TaskState.Play;
            try
            {
                //_log.Info("Combinaition:"+_start.ToString());
                FindSolution();
                if (Success == true)
                {
                    State = TaskState.End;
                    _log.Info("Succes:" + _start.ToString());
                    State = TaskState.End;
                    if (OnComplete != null)
                    {
                        OnComplete(this);
                    }
                    return;
                }
                
                this.FirstCombination(_start);
                _work[4].Rotate();
                FindSolution();
                if (Success == true)
                {
                    State = TaskState.End;
                    _log.Info("Succes:" + _start.ToString());
                    State = TaskState.End;
                    if (OnComplete != null)
                    {
                        OnComplete(this);
                    }
                    return;
                }
                this.FirstCombination(_start);
                _work[4].Rotate();
                _work[4].Rotate();
                FindSolution();
                if (Success == true)
                {
                    State = TaskState.End;
                    _log.Info("Succes:" + _start.ToString());
                    State = TaskState.End;
                    if (OnComplete != null)
                    {
                        OnComplete(this);
                    }
                    return;
                }
                this.FirstCombination(_start);
                _work[4].Rotate();
                _work[4].Rotate();
                _work[4].Rotate();
                FindSolution();
                if (Success == true)
                {                    
                    _log.Info("Succes:" + _start.ToString());
                }
            }
            catch (Exception e)
            {
                _log.Error("Task:" + Name + " - " + e.Message);
            }
            State = TaskState.End;

            if (OnComplete != null)
            {
                OnComplete(this);
            }

        }

        // spusti jako task
        public void StartTask()
        {
            Task t = new Task(new Action(() =>
            {
                
                State = TaskState.Play;
                try
                {
                    FindSolution();
                }
                catch (Exception e)
                {
                    _log.Error("Task:" + Name + " - " + e.Message);
                }
                State = TaskState.End;
            }));
            t.Start();
        }

        private void FindSolution()
        {
            if (_work == null)
            {
              _work = new SolutionCombination(_start);
            }

            /*
            foreach (OneCard c_test in _work)
            {
                if (c_test == null)
                {
                    //_work = new SolutionCombination(_start);
                    //break;
                    return;
                }
            }*/

            OneCard center = _work[4];
            List<OneCard> top = null;
            List<OneCard> right = null;
            List<OneCard> bottom = null;
            List<OneCard> left = null;
            ///
            OneCard c = _work[1];

            try
            {
                SmileDelegate sm = new SmileDelegate(c.GetBottom);
                top = FindSmile(c, center.Top, sm);
                ///
                c = _work[5];
                sm = new SmileDelegate(c.GetLeft);
                right = FindSmile(c, center.Right, sm);
                ///
                c = _work[7];
                sm = new SmileDelegate(c.GetTop);
                bottom = FindSmile(c, center.Bottom, sm);
                ///
                c = _work[3];
                sm = new SmileDelegate(c.GetRight);
                left = FindSmile(c, center.Left, sm);
            }
            catch (Exception e)
            {
                _log.Error(e.Message,"FindSolution");
            }
            ///
            List<OneCard> lefttop1 = null;
            List<OneCard> lefttop2 = null;
            //
            List<OneCard> righttop1 = null;
            List<OneCard> righttop2 = null;
            //
            List<OneCard> rightbot1 = null;
            List<OneCard> rightbot2 = null;
            //
            List<OneCard> leftbot1 = null;
            List<OneCard> leftbot2 = null;
            // 
            OneCard res_lt = null;
            OneCard res_rt = null;
            OneCard res_rb = null;
            OneCard res_lb = null;
            //
            OneCard res_left = null;
            OneCard res_top = null;
            OneCard res_bottom = null;
            OneCard res_right = null;
            //                                    

            if (left != null && bottom!=null && right!=null && top != null &&left.Count > 0 && bottom.Count > 0 && right.Count > 0 && top.Count > 0)
            {
                //TOP - porovnavam left_top, right_top 
                try
                {
                    foreach (OneCard top_c in top)
                    {
                        OneCard le = _work[0];
                        SmileDelegate sm_left = new SmileDelegate(le.GetRight);
                        lefttop1 = FindSmile(le, top_c.Left, sm_left);
                        //
                        OneCard ri = _work[2];
                        SmileDelegate sm_right = new SmileDelegate(ri.GetLeft);
                        righttop1 = FindSmile(ri, top_c.Right, sm_right);

                        if (lefttop1.Count > 0 && righttop1.Count > 0)
                        {
                            res_top = top_c;
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error(e.Message, "FindSolution.TOP");
                }

                // RIGHT  - porovnavam right_top, right_bottom
                try
                {
                    foreach (OneCard right_c in right)
                    {
                        OneCard rt = _work[2];
                        SmileDelegate sm_top = new SmileDelegate(rt.GetBottom);
                        righttop2 = FindSmile(rt, right_c.Top, sm_top);
                        //
                        OneCard rb = _work[8];
                        SmileDelegate sm_bot = new SmileDelegate(rb.GetTop);
                        rightbot1 = FindSmile(rb, right_c.Bottom, sm_bot);

                        if (righttop2.Count > 0 && rightbot1.Count > 0)
                        {
                            res_right = right_c;
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error(e.Message, "FindSolution.RIGHT");
                }
                // BOTTOM - porovnavam left_bottom, right_bottom
                try
                {
                    foreach (OneCard bottom_c in bottom)
                    {
                        OneCard lb = _work[6];
                        SmileDelegate sm_left = new SmileDelegate(lb.GetRight);
                        leftbot1 = FindSmile(lb, bottom_c.Left, sm_left);
                        //
                        OneCard rb = _work[8];
                        SmileDelegate sm_bot = new SmileDelegate(rb.GetLeft);
                        rightbot2 = FindSmile(rb, bottom_c.Right, sm_bot);

                        if (leftbot1.Count > 0 && rightbot2.Count > 0)
                        {
                            res_bottom = bottom_c;
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error(e.Message, "FindSolution.BOTTOM");
                }

                // LEFT - porovnavam left_bottom, left_top
                try
                {
                    foreach (OneCard left_c in left)
                    {
                        OneCard lt = _work[0];
                        SmileDelegate sm_top = new SmileDelegate(lt.GetBottom);
                        lefttop2 = FindSmile(lt, left_c.Top, sm_top);
                        //
                        OneCard lb = _work[6];
                        SmileDelegate sm_bot = new SmileDelegate(lb.GetTop);
                        leftbot2 = FindSmile(lb, left_c.Bottom, sm_bot);

                        if (lefttop2.Count > 0 && leftbot2.Count > 0)
                        {
                            res_left = left_c;
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error(e.Message, "FindSolution.LEFT");
                }

                try
                {
                    // LEFTTOP
                    if (lefttop1 != null && lefttop2 != null)
                    {
                        foreach (OneCard lt1 in lefttop1)
                        {
                            foreach (OneCard lt2 in lefttop2)
                            {
                                if (lt1.Equals(lt2) == true)
                                {
                                    res_lt = lt1;
                                }
                            }
                        }
                    }
                    // RIGHTTOP
                    if (righttop1 != null && righttop2 != null)
                    {
                        foreach (OneCard rt1 in righttop1)
                        {
                            foreach (OneCard rt2 in righttop2)
                            {
                                if (rt1.Equals(rt2))
                                    res_rt = rt1;
                            }
                        }
                    }
                    // RIGHTBOTTOM
                    if (rightbot1 != null && rightbot2 != null)
                    {
                        foreach (OneCard rb1 in rightbot1)
                        {
                            foreach (OneCard rb2 in rightbot2)
                            {
                                if (rb1.Equals(rb2))
                                    res_rb = rb1;
                            }
                        }
                    }
                    // LEFTBOTTOM
                    if (leftbot1 != null && leftbot2 != null)
                    {
                        foreach (OneCard lb1 in leftbot1)
                        {
                            foreach (OneCard lb2 in leftbot2)
                            {
                                if (lb1.Equals(lb2))
                                    res_lb = lb1;
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error(e.Message, "FindSolution.ROHY");                
                }

                if (res_lt != null && res_top != null && res_rt != null && res_left != null && res_right != null && res_lb != null && res_bottom != null && res_rb != null)
                {
                    _work[0] = res_lt;
                    _work[1] = res_top;
                    _work[2] = res_rt;
                    _work[3] = res_left;
                    _work[5] = res_right;
                    _work[6] = res_lb;
                    _work[7] = res_bottom;
                    _work[8] = res_rb;
                }

                Success = _work.CheckSolution();
                if (Success == true)
                {
                    Solution = _work;
                }
            }
            ///
        }

        /// <summary>
        /// Najdi si svuj usmev :(, :)
        /// </summary>
        /// <param name="hledam"></param>
        /// <param name="cental"></param>
        /// <param name="sm">delegat na vedlejsi porovnavanou kartu, mohu porovnat odpovidajici oblicej</param>
        /// <returns></returns>
        private List<OneCard> FindSmile(OneCard hledam, HalfSmile cental, SmileDelegate sm)
        {
            Debug.Assert(sm != null);
            Debug.Assert(hledam != null);
            Debug.Assert(cental != null);
            
            List<OneCard> result = new List<OneCard>();
            int rozdil = cental.Value + sm().Value;
            if (rozdil == 0)
                result.Add(new OneCard(hledam));
            
            hledam.Rotate();
            rozdil = cental.Value + sm().Value;
            if (rozdil == 0)
                result.Add(new OneCard(hledam));

            hledam.Rotate();
            rozdil = cental.Value + sm().Value;
            if (rozdil == 0)
                result.Add(new OneCard(hledam));

            hledam.Rotate();
            rozdil = cental.Value + sm().Value;
            if (rozdil == 0)
                result.Add(new OneCard(hledam));

            return result;
        }

        SolutionCombination _start; // startovaci kombinace
        SolutionCombination _work; // pracovni kombinace
        /// <summary>
        /// Nastaveni vychozi kombinace, kt. se zkopiruje pro praci.
        /// </summary>
        /// <param name="start"></param>
        public void FirstCombination(SolutionCombination start)
        {
            _start = start;            
            _solution = _start;
            if (_work == null)
            {
                _work = new SolutionCombination(_start);
            }
        }

        public void Clear()
        {
            //_start = null;
            _work = null;            
        }

        // EVENTS

        /// <summary>
        /// Udalost - skoncil jsem s ukolem
        /// </summary>
        public event CompleteTaskHandler OnComplete;
        //PROPERTIES
        
        /// <summary>
        /// Vychozi kombinace
        /// </summary>
        public SolutionCombination Combination { get { return _start; } }


        private int _progress = 0;
        /// <summary>
        /// Progress ukolu
        /// </summary>
        public int Progress
        {
            get { return _progress; }
            set
            {
                _progress = value;
                OnPropertyChanged("Progress");
            }
        }

        TaskState _state = TaskState.Stop;
        /// <summary>
        /// Stav ukolu
        /// </summary>
        public TaskState State
        {
            get { return _state; }
            set
            {
                _state = value;
                OnPropertyChanged("State");
                OnPropertyChanged("StateImage");
            }
        }

        BitmapSource _source = null;
        /// <summary>
        /// Obrazek stavu
        /// </summary>
        public BitmapSource StateImage
        {
            get
            {
                if (_source != null)
                    _source.Freeze();

                if (_state == TaskState.Play)
                {                    
                    BitmapImage bi = new BitmapImage();                    
                    bi.BeginInit();
                    bi.UriSource = new Uri(@"/Solver;component/Images/play.png", UriKind.RelativeOrAbsolute);
                    bi.EndInit();
                    _source = bi;
                    return bi;
                }
                else if (_state == TaskState.Stop)
                {
                    BitmapImage bi = new BitmapImage();                    
                    bi.BeginInit();
                    bi.UriSource = new Uri(@"/Solver;component/Images/stop.png", UriKind.RelativeOrAbsolute);
                    bi.EndInit();
                    _source = bi;
                    return bi;
                }
                else if (_state == TaskState.End)
                {
                    BitmapImage bi = new BitmapImage();                    
                    bi.BeginInit();
                    bi.UriSource = new Uri(@"/Solver;component/Images/finish.png", UriKind.RelativeOrAbsolute);
                    bi.EndInit();
                    _source = bi;
                    return bi;
                }
                else
                {
                    BitmapImage bi = new BitmapImage();                   
                    bi.BeginInit();
                    bi.UriSource = new Uri(@"/Solver;component/Images/finish.png", UriKind.RelativeOrAbsolute);
                    bi.EndInit();
                    _source = bi;
                    return bi;
                }
            }
        }

        // Pojmenovani
        public string Name
        {
            get;
            set;
        }

        private SolutionCombination _solution = null;
        /// <summary>
        /// Instance uspesneho reseni
        /// </summary>
        public SolutionCombination Solution
        {
            get { return _solution; }
            set
            {
                _solution = value;
                OnPropertyChanged("Solution");
                OnPropertyChanged("SolutionString");
            }
        }

        /// <summary>
        /// retezec zobrazujici reseni
        /// </summary>
        public string SolutionString
        {
            get 
            {
                if (_solution == null)
                    return "";

                return _solution.ToString(); 
            }
        }

        private bool _success = false;
        /// <summary>
        /// Indikator uspesneho reseni 
        /// </summary>        
        public bool Success
        {
            get { return _success; }
            set
            {
                _success = value;
                OnPropertyChanged("Success");
            }
        }

        public Visibility IsVisible
        {
            get
            {
                return Visibility.Collapsed;
            }
        }

        public override string ToString()
        {

            string ret = "";
            if (_start != null)
                ret = this._start.ID + ". Task:" + _start.ToString();
            if (Solution != null)
            {
                ret += "\nSolution:" + SolutionString;
            }
            return ret;
        }
    }
}
